﻿
#region 文件信息
/*----------------------------------------------------------------
// 
// 文件名称：	
// 文件功能描述：	
// 设计要求：	
//
// 文 件 名：    MapperManager
// 创建者：      杨程
// 创建日期：	    2022/10/24 16:59:55

//----------------------------------------------------------------*/
#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using static System.Linq.Expressions.Expression;



namespace Vampirewal.Core.Mapper;

/// <summary>
/// 映射管理器
/// </summary>
public static class MapperManager
{


    static Dictionary<Type, Type> MapperDic = new Dictionary<Type, Type>();

    #region 注册(Register)
    public static void RegisterAll()
    {
        /*
         * 注意事项：
         *     1、如果是WPF使用的话，建议把ViewModelLocator放在VM类库中
         *     2、然后再调用这个，才能在初始化的时候，找到VM这个类库，然后加载里面的ViewModel，不然的话，找不到VM这个程序集，会无法加载ViewModel
         */



        var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(w => w.GetInterface("IVampirewalMapper") != null).ToList();



        foreach (var v in types)
        {
            var aaa = Activator.CreateInstance(v) as IVampirewalMapper;

            var target = aaa.MapperTargetType();

            MapperDic.Add(v, target);
        }
    }
    #endregion

    #region 属性

    #endregion

    #region 公共方法

    public static TTarget Map<Tsource, TTarget>(Tsource source) where Tsource : class, new() where TTarget : class, new()
    {
        //MapperDic.TryGetValue(typeof(Tsource), out Type target);

        var result = Activator.CreateInstance(typeof(TTarget));

        var SourceProps = typeof(Tsource).GetProperties();
        var TargetProps = typeof(TTarget).GetProperties();

        foreach (var prop1 in SourceProps)
        {
            foreach (var prop2 in TargetProps)
            {
                if (prop2.PropertyType.IsValueType || prop2.PropertyType.Name.StartsWith("String"))
                {
                    if (prop1.Name == prop2.Name)
                    {
                        try
                        {
                            object value = prop1.GetValue(source, null);
                            var prop = typeof(TTarget).GetProperty(prop1.Name);
                            if (prop != null && prop.CanWrite && !(value is DBNull))
                            {
                                prop.SetValue(result, value, null);
                            }
                        }
                        catch (Exception)
                        {

                            throw;
                        }
                    }
                }
            }
        }

        return (TTarget)result;
    }

    public static object Map<TSource>(TSource source)
    {

        if (MapperDic.TryGetValue(typeof(TSource), out Type target))
        {
            var result = Activator.CreateInstance(target);

            var SourceProps = typeof(TSource).GetProperties();
            var TargetProps = target.GetProperties();

            foreach (var prop1 in SourceProps)
            {
                foreach (var prop2 in TargetProps)
                {
                    if (prop2.PropertyType.IsValueType || prop2.PropertyType.Name.StartsWith("String"))
                    {
                        if (prop1.Name == prop2.Name)
                        {
                            try
                            {
                                object value = prop1.GetValue(source, null);
                                var prop = target.GetProperty(prop1.Name);
                                if (prop != null && prop.CanWrite && !(value is DBNull))
                                {
                                    prop.SetValue(result, value, null);
                                }
                            }
                            catch (Exception)
                            {

                                throw;
                            }
                        }
                    }
                }
            }

            return result;
        }
        else
        {
            throw new Exception("未注册目标类型");
        }


    }

    /// <summary>
    /// 简单映射
    /// </summary>
    /// <typeparam name="TTarget">目标类型</typeparam>
    /// <param name="source"></param>
    /// <returns></returns>
    public static TTarget SimpleMap<TTarget>(this object source)
    {
        var result = Activator.CreateInstance(typeof(TTarget));

        var SourceProps = source.GetType().GetProperties();
        var TargetProps = typeof(TTarget).GetProperties();

        foreach (var prop1 in SourceProps)
        {
            foreach (var prop2 in TargetProps)
            {
                if (prop2.PropertyType.IsValueType || prop2.PropertyType.Name.StartsWith("String"))
                {
                    if (prop1.Name == prop2.Name)
                    {
                        try
                        {
                            object value = prop1.GetValue(source, null);
                            var prop = typeof(TTarget).GetProperty(prop1.Name);
                            if (prop != null && prop.CanWrite && !(value is DBNull))
                            {
                                prop.SetValue(result, value, null);
                            }
                        }
                        catch (Exception ex)
                        {

                            throw ex;
                        }
                    }
                }
            }
        }

        return (TTarget)result;
    }

    #endregion

    #region 私有方法

    #endregion

    #region Command命令

    #endregion
}


public static class Mapper<TSource, TTarget> where TSource : class where TTarget : class
{
    public readonly static Func<TSource, TTarget> MapFunc = GetMapFunc();

    public readonly static Action<TSource, TTarget> MapAction = GetMapAction();

    /// <summary>
    /// 将对象TSource转换为TTarget
    /// </summary>
    /// <param name="source"></param>
    /// <returns></returns>
    public static TTarget Map(TSource source) => MapFunc(source);

    public static List<TTarget> MapList(IEnumerable<TSource> sources) => sources.Select(MapFunc).ToList();



    /// <summary>
    /// 将对象TSource的值赋给给TTarget
    /// </summary>
    /// <param name="source"></param>
    /// <param name="target"></param>
    public static void Map(TSource source, TTarget target) => MapAction(source, target);

    private static Func<TSource, TTarget> GetMapFunc()
    {
        var sourceType = typeof(TSource);
        var targetType = typeof(TTarget);
        //Func委托传入变量
        var parameter = Parameter(sourceType, "p");

        var memberBindings = new List<MemberBinding>();
        var targetTypes = targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite);
        foreach (var targetItem in targetTypes)
        {
            var sourceItem = sourceType.GetProperty(targetItem.Name);

            //判断实体的读写权限
            if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic)
                continue;

            //标注NotMapped特性的属性忽略转换
            if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null)
                continue;

            var sourceProperty = Property(parameter, sourceItem);

            //当非值类型且类型不相同时
            if (!sourceItem.PropertyType.IsValueType && sourceItem.PropertyType != targetItem.PropertyType)
            {
                //判断都是(非泛型)class
                if (sourceItem.PropertyType.IsClass && targetItem.PropertyType.IsClass &&
                    !sourceItem.PropertyType.IsGenericType && !targetItem.PropertyType.IsGenericType)
                {
                    var expression = GetClassExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
                    memberBindings.Add(Bind(targetItem, expression));
                }

                //集合数组类型的转换
                if (typeof(IEnumerable).IsAssignableFrom(sourceItem.PropertyType) && typeof(IEnumerable).IsAssignableFrom(targetItem.PropertyType))
                {
                    var expression = GetListExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
                    memberBindings.Add(Bind(targetItem, expression));
                }

                continue;
            }

            if (targetItem.PropertyType != sourceItem.PropertyType)
                continue;

            memberBindings.Add(Bind(targetItem, sourceProperty));
        }

        //创建一个if条件表达式
        var test = NotEqual(parameter, Constant(null, sourceType));// p==null;
        var ifTrue = MemberInit(New(targetType), memberBindings);
        var condition = Condition(test, ifTrue, Constant(null, targetType));

        var lambda = Lambda<Func<TSource, TTarget>>(condition, parameter);
        return lambda.Compile();
    }

    /// <summary>
    /// 类型是clas时赋值
    /// </summary>
    /// <param name="sourceProperty"></param>
    /// <param name="targetProperty"></param>
    /// <param name="sourceType"></param>
    /// <param name="targetType"></param>
    /// <returns></returns>
    private static Expression GetClassExpression(Expression sourceProperty, Type sourceType, Type targetType)
    {
        //条件p.Item!=null    
        var testItem = NotEqual(sourceProperty, Constant(null, sourceType));

        //构造回调 Mapper<TSource, TTarget>.Map()
        var mapperType = typeof(Mapper<,>).MakeGenericType(sourceType, targetType);
        var iftrue = Call(mapperType.GetMethod(nameof(Map), new[] { sourceType }), sourceProperty);

        var conditionItem = Condition(testItem, iftrue, Constant(null, targetType));

        return conditionItem;
    }

    /// <summary>
    /// 类型为集合时赋值
    /// </summary>
    /// <param name="sourceProperty"></param>
    /// <param name="targetProperty"></param>
    /// <param name="sourceType"></param>
    /// <param name="targetType"></param>
    /// <returns></returns>
    private static Expression GetListExpression(Expression sourceProperty, Type sourceType, Type targetType)
    {
        //条件p.Item!=null    
        var testItem = NotEqual(sourceProperty, Constant(null, sourceType));

        //构造回调 Mapper<TSource, TTarget>.MapList()
        var sourceArg = sourceType.IsArray ? sourceType.GetElementType() : sourceType.GetGenericArguments()[0];
        var targetArg = targetType.IsArray ? targetType.GetElementType() : targetType.GetGenericArguments()[0];
        var mapperType = typeof(Mapper<,>).MakeGenericType(sourceArg, targetArg);

        var mapperExecMap = Call(mapperType.GetMethod(nameof(MapList), new[] { sourceType }), sourceProperty);

        Expression iftrue;
        if (targetType == mapperExecMap.Type)
        {
            iftrue = mapperExecMap;
        }
        else if (targetType.IsArray)//数组类型调用ToArray()方法
        {
            iftrue = Call(mapperExecMap, mapperExecMap.Type.GetMethod("ToArray"));
        }
        else if (typeof(IDictionary).IsAssignableFrom(targetType))
        {
            iftrue = Constant(null, targetType);//字典类型不转换
        }
        else
        {
            iftrue = Convert(mapperExecMap, targetType);
        }

        var conditionItem = Condition(testItem, iftrue, Constant(null, targetType));

        return conditionItem;
    }

    private static Action<TSource, TTarget> GetMapAction()
    {
        var sourceType = typeof(TSource);
        var targetType = typeof(TTarget);
        //Func委托传入变量
        var sourceParameter = Parameter(sourceType, "p");

        var targetParameter = Parameter(targetType, "t");

        //创建一个表达式集合
        var expressions = new List<Expression>();

        var targetTypes = targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite);
        foreach (var targetItem in targetTypes)
        {
            var sourceItem = sourceType.GetProperty(targetItem.Name);

            //判断实体的读写权限
            if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic)
                continue;

            //标注NotMapped特性的属性忽略转换
            if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null)
                continue;

            var sourceProperty = Property(sourceParameter, sourceItem);
            var targetProperty = Property(targetParameter, targetItem);

            //当非值类型且类型不相同时
            if (!sourceItem.PropertyType.IsValueType && sourceItem.PropertyType != targetItem.PropertyType)
            {
                //判断都是(非泛型)class
                if (sourceItem.PropertyType.IsClass && targetItem.PropertyType.IsClass &&
                    !sourceItem.PropertyType.IsGenericType && !targetItem.PropertyType.IsGenericType)
                {
                    var expression = GetClassExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
                    expressions.Add(Assign(targetProperty, expression));
                }

                //集合数组类型的转换
                if (typeof(IEnumerable).IsAssignableFrom(sourceItem.PropertyType) && typeof(IEnumerable).IsAssignableFrom(targetItem.PropertyType))
                {
                    var expression = GetListExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
                    expressions.Add(Assign(targetProperty, expression));
                }

                continue;
            }

            if (targetItem.PropertyType != sourceItem.PropertyType)
                continue;


            expressions.Add(Assign(targetProperty, sourceProperty));
        }

        //当Target!=null判断source是否为空
        var testSource = NotEqual(sourceParameter, Constant(null, sourceType));
        var ifTrueSource = Block(expressions);
        var conditionSource = IfThen(testSource, ifTrueSource);

        //判断target是否为空
        var testTarget = NotEqual(targetParameter, Constant(null, targetType));
        var conditionTarget = IfThen(testTarget, conditionSource);

        var lambda = Lambda<Action<TSource, TTarget>>(conditionTarget, sourceParameter, targetParameter);
        return lambda.Compile();
    }
}
